/***** server code *****
 * Usage:
 * reading from given data file, and dispatch to two clients;
 * accepts input and query if the input item exists in any clients.
 * -1 is the exit number;
 * -2 use to notify clients data sending is over, and query part starts.
 *
 * Created by Li ZHOU, Mar 18
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

//show usage of prog
void usage(char * n) {
    fprintf(stderr, "usage:%s [data_file]\n", n);
}

//Get return info from client: -2: err; -1: not exist; other: position
int exist_in(int fd);

//Send signal to both client1 and client2
void signal_client(int fd_1, int fd_2, int signal);


//main function
int main( int argc, char * argv[] ) {
	/* IF # of args is incorrect */
    if(argc != 2) {
		usage(argv[0]);
		exit(1);
	}
    printf("Server pid: %d\n", getpid());
	pid_t c_pid;
	/* PIPE to clients */
    int fd_out_client1[2];
	int fd_in_client1[2];
    int fd_out_client2[2];
	int fd_in_client2[2];

	/* build pipe for client 1 */
	if( pipe(fd_out_client1) < 0 ) {
		fprintf(stderr, "PIPE building failed.\n");
		exit(1);
	}
    if( pipe(fd_in_client1) < 0) {
        fprintf(stderr, "PIPE building failed.\n");
		exit(1);
    }
	/* fork err handle for client 1 */
    if( (c_pid = fork()) < 0 ) {
       fprintf(stderr, "fork err.\n");
	   exit(1);
    }
	/* Server, the first fork() for client 1 */
	else if( c_pid > 0) {
	    /* Build pipe for client 2 */
		if( pipe(fd_out_client2) < 0 ) {
			fprintf(stderr, "PIPE building failed.\n");
			exit(1);
		}
		if( pipe(fd_in_client2) < 0) {
			fprintf(stderr, "PIPE building failed.\n");
			exit(1);
		}
       /* fork() err handle for client 2 */ 
       if( (c_pid = fork()) < 0 ) {
		   fprintf(stderr, "fork err.\n");
		   exit(1);
	   }
	   /* Server , the second fork() for client 2 */
	   else if ( c_pid > 0 ) {
		   /* close input end */
		   //client 1
		   close(fd_out_client1[0]); // reading end of pipe: s->c
		   close(fd_in_client1[1]);  // writing end of pipe: c->s
		   //client 2
		   close(fd_out_client2[0]); // reading end of pipe: s->c
		   close(fd_in_client2[1]);  // writing end of pipe: c->s
           
		   /* Initial server variables */
		   int how_many = 0;							// total number of items in file
           int next_step = -2;							// indicates to query
		   int finish = -1;								// indicates to finish
           ssize_t bytes_in_pipe;						// test pipe operation result
		   
		   FILE * file_in_ptr = NULL;
		   file_in_ptr = fopen(argv[1], "r");

           if(file_in_ptr == NULL) {
			   fprintf(stderr, "Open file failed.\n");
               signal_client(fd_out_client1[1], fd_out_client2[1], finish);
			   exit(1);
		   }
		   int item_in_each = 0;
		   /* initial the number of items for clients to allocate data space */
		   if( fscanf(file_in_ptr, "%d", &how_many) != EOF && how_many > 0 ) {
				/* provide the estimate number of items to client */
                item_in_each = how_many/2 + 1;
			    printf("File has %d items. for each client is %d.\n", how_many, item_in_each);
                bytes_in_pipe = write(fd_out_client1[1], &item_in_each, sizeof(int));
				if(bytes_in_pipe <= 0 ) {
                    fprintf(stderr, "error pipe operation.\n");
                    signal_client(fd_out_client1[1], fd_out_client2[1], finish);
					exit(1);
				}
				bytes_in_pipe = write(fd_out_client2[1], &item_in_each, sizeof(int));
                if(bytes_in_pipe <= 0 ) {
                    fprintf(stderr, "error pipe operation.\n");
                    signal_client(fd_out_client1[1], fd_out_client2[1], finish);
                    exit(1);
				}
		   }
		   /* file is empty or number of items is incorrect */
		   else {
			   fprintf(stderr, "Empty file.\n");
               signal_client(fd_out_client1[1], fd_out_client2[1], finish); 
			   exit(0);
		   }
           int item;
           int cnt = 0;
		   int last_to = 2;
           while( fscanf(file_in_ptr, "%d", &item) != EOF ) {
			    // mismatch number of items
                if( cnt > how_many) {
					fprintf(stderr, "File has items more than it claimed.\n");
                    signal_client(fd_out_client1[1], fd_out_client2[1], finish); 
					exit(1);
				}
				//if last is send to client2 or initial, send to client 1
				else if( last_to == 2) {
					bytes_in_pipe = write(fd_out_client1[1], &item, sizeof(int));
					cnt++;
					last_to = 1;
				}
				//if last is send to client1, send to client 2
		        else if( last_to == 1) {
					bytes_in_pipe = write(fd_out_client2[1], &item, sizeof(int));
                    cnt++;
					last_to = 2;
				}
				else {
                    fprintf(stderr, "Unknown err, current item: %d\n", item); 
                    signal_client(fd_out_client1[1], fd_out_client2[1], finish); 
                    exit(1);
				}
           }

		   fclose(file_in_ptr);

           if( cnt < how_many ) {
			   fprintf(stderr, "less than claimed items.\n");
               signal_client(fd_out_client1[1], fd_out_client2[1], finish); 
			   exit(1);
		   }
		   /***** Query part *****/ 
		   printf("Start to get query....\n");
		   signal_client(fd_out_client1[1], fd_out_client2[1], next_step); 
		   int query_num = 0;
		   int pos = 0;
           while( 1 ) {
		       printf("Provide a 5 digit number to query[input -1 to finish]:\n");
			   scanf("%d", &query_num);
			   int result = 0;
			   // finsh and exit;
			   if(query_num == -1) {
				   printf("receiving -1, exit()\n");
                   signal_client(fd_out_client1[1],
						         fd_out_client2[1],
								 finish);
				   close(fd_out_client1[1]);
				   close(fd_in_client1[0]);
				   close(fd_out_client2[1]);
				   close(fd_in_client2[0]);
				   break;
			   }
			   //query a number
			   else {
				   //send the query number
				   signal_client(fd_out_client1[1],
						         fd_out_client2[1],
								 query_num);
				   //searching in client 1
				   pos = exist_in(fd_in_client1[0]);
				   //err handle
                   if( pos == -2 ) {
                       signal_client(fd_out_client1[1],
							         fd_out_client2[1],
									 finish);
					   exit(1);
				   }
				   //find in client 1;
				   else if( pos >= 0) {
					   printf("Find number %d in client 1 at pos = %d\n", query_num, pos); 
					   result = 1;
				   }
				   else {
					   ;
				   }

				   //searching in client 2 
				   pos = exist_in(fd_in_client2[0]); 
				   //err handle
				   if( pos == -2 ) {
					   signal_client(fd_out_client1[1],
									 fd_out_client2[1],
									 finish);
					   exit(1);
				   }
				   //find in client 2
				   else if( pos >= 0) {
					   printf("Find number %d in client 2 at pos = %d\n", query_num,pos); 
					   result = 1;
				   }
				   //not exist
				   else {
					   ;
				   }
				   /*** OUTPUT result ***/
                   if( result == 0)  
				       printf("No such item exists.\n");
			   }
           }
	   }
	   /** Client 2 **/
	   else {
		   printf("\tclient2 pid: %d\n",getpid());
		   //redirect client2 stdin to pipe_out[0] 
		   close(fd_out_client2[1]);
           dup2(fd_out_client2[0], STDIN_FILENO);
		   close(fd_out_client2[0]);
		   
		   //redirect client2 stdout to pipe_in[0]
		   close(fd_in_client2[0]);
		   dup2(fd_in_client2[1], STDOUT_FILENO);
		   close(fd_in_client2[1]);
           
		   //close unrelative pipe ends
           close(fd_in_client1[0]);
		   close(fd_in_client1[1]);
		   close(fd_out_client1[0]);
		   close(fd_out_client1[1]);

           if(execl("./client", "client", NULL) == -1)
			   perror("execl error");
       }
	}
	/* client 1 */
    else {
		printf("\tclient1 pid: %d\n",getpid());
        /* redirect the STDIN to input pipe reading end */
        close(fd_out_client1[1]);
		dup2(fd_out_client1[0], STDIN_FILENO);
		close(fd_out_client1[0]);
		
		/* redirect the STDOUT to output pipe writing end */ 
		close(fd_in_client1[0]);
        dup2(fd_in_client1[1], STDOUT_FILENO);
		close(fd_in_client1[1]);
		
		if(execl("./client", "client", NULL) == -1)
			perror("execl error");
    }
	return 0;
}


int exist_in(int fd) {
    int rtn = 0;
	ssize_t b = read(fd, &rtn, sizeof(int));
	if(b <= 0)
		return -2;
	else
		return rtn;
}

void signal_client(int fd_1, int fd_2, int signal) {
    ssize_t s = 0;
	while(s == 0) {
       s = write(fd_1, &signal, sizeof(int));
    }
    s = 0;
	sleep(1);
	while(s == 0) {
       s = write(fd_2, &signal, sizeof(int));
	}
}
